﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using Lm.Common.ExtensionMethods;

namespace Lm.Common.Net
{
    public class Client : IDisposable
    {
        protected IPAddress ServerIp { get; set; }
        protected int ServerPort { get; set; }

        protected object LockObj { get; set; }

        protected TcpClient TcpClient { get; set; }
        protected EncryptedNetworkStream SafeStream { get; set; }


        protected Client()
        { }

        public Client(string serverIp, int serverPort)
        {
            this.LockObj = new object();
            this.ServerIp = IPAddress.Parse(serverIp);
            this.ServerPort = serverPort;
        }

        public T Request<T>(IRequestMessage<T> requestMessage) where T : IResponseMessage, new()
        {
            lock (this.LockObj)
            {
                try
                {
                    if (this.TcpClient == null)
                    {
                        var tcpClient = new TcpClient();
                        tcpClient.Connect(this.ServerIp, this.ServerPort);
                        var safeStream = tcpClient.GetEncryptedStream();
                        safeStream.StartEncrypt();
                        this.SafeStream = safeStream;
                        this.TcpClient = tcpClient;
                    }
                    var requestBuf = new Serialization.BinaryFormatter().Serialize(requestMessage);
                    this.SafeStream.WriteBlock(requestBuf);
                    var responseBuf = this.SafeStream.ReadBlock();
                    var result = new Serialization.BinaryFormatter().Deserialize<IResponseMessage>(responseBuf);
                    if (result.GetType() == typeof(ErrResponseMessage))
                    {
                        throw new Exception(((ErrResponseMessage)result).ErrMessage);
                    }
                    else
                    {
                        return (T)result;
                    }
                }
                catch (IOException ee)
                {
                    this.Dispose();
                    throw ee;
                }
                catch (SocketException ee)
                {
                    this.Dispose();
                    throw ee;
                }
                catch (ObjectDisposedException ee)
                {
                    this.Dispose();
                    throw ee;
                }
                catch (Exception ee)
                { throw ee; }
            }
        }

        #region IDisposable 成员

        public void Dispose()
        {
            lock (this.LockObj)
            {
                if (this.TcpClient != null)
                {
                    this.TcpClient.Close();
                    this.TcpClient = null;
                    this.SafeStream = null;
                }
            }
        }

        #endregion
    }
}
